/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package MonkeyStuff;

import B3D_Elements.B3D_Element;
import B3D_Elements.B3D_Spatials.B3D_Geometrys.B3D_Geometry;
import B3D_Elements.B3D_Spatials.B3D_Node;
import B3D_Elements.B3D_Spatials.B3D_Spatial;
import B3D_Elements.Others.B3D_MotionEvent;
import GUI.Dialogs.AdditionalCameraDialog;
import General.CurrentData;
import com.jme3.app.SimpleApplication;
import com.jme3.asset.plugins.FileLocator;
import com.jme3.bounding.BoundingBox;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.cinematic.events.MotionEvent;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.input.ChaseCamera;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.light.Light;
import com.jme3.light.PointLight;
import com.jme3.light.SpotLight;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Ray;
import com.jme3.math.Spline;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.CameraControl.ControlDirection;
import com.jme3.scene.debug.Grid;
import com.jme3.scene.debug.WireBox;
import com.jme3.scene.shape.Curve;
import com.jme3.scene.shape.Line;
import com.jme3.scene.shape.Sphere;
import com.jme3.system.AppSettings;
import com.jme3.texture.Texture2D;
import com.jme3.ui.Picture;
import com.jme3.water.WaterFilter;
import other.ElementList;
import other.Wizard;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.Callable;
import org.lwjgl.input.Mouse;
import tonegod.gui.controls.buttons.Button;
import tonegod.gui.controls.buttons.ButtonAdapter;
import tonegod.gui.controls.menuing.Menu;
import tonegod.gui.core.Screen;

/**
 *
 * @author David
 */
public class B3DApplication extends SimpleApplication implements ActionListener
{

    private SceneNode sceneNode = new SceneNode();
    private Node editorNode = new Node("EditorNode");
    private DirectionalLight editorNodeCamLight = new DirectionalLight();
    private Node motionEventNode = new Node("Motion Events");
    private Node selectedNode = sceneNode;
    private BulletAppState bulletAppState;
    private Vector<MotionEvent> motionEvents = new Vector<MotionEvent>();
    private ChaseCamera chaseCamera;
    private CameraNode camNode;
    private Vector<NodeModel> nodeModels = new Vector<NodeModel>();
    private Vector<LightModel> lightModels = new Vector<LightModel>();
    private Vector<LightScatteringModel> lightScatteringModels = new Vector<LightScatteringModel>();
    private Node arrowNode = new Node("Arrows");
    private Spatial xArrow;
    private Spatial yArrow;
    private Spatial zArrow;
    private Material xArrowMaterial;
    private Material yArrowMaterial;
    private Material zArrowMaterial;
    private Spatial selectedArrow;
    private WireBox selectionWireBox = new WireBox();
    private Geometry selectionBox = new Geometry("SelectionBox", selectionWireBox);
    private Geometry gridGeometry = new Geometry("nonI_Grid", new Grid(
            Integer.parseInt((String) CurrentData.getConfiguration().getSettings().get("gridx")),
            Integer.parseInt((String) CurrentData.getConfiguration().getSettings().get("gridy")),
            Float.parseFloat((String) CurrentData.getConfiguration().getSettings().get("gridgap"))));
    private ElementList elementList = new ElementList();
    private B3D_Element selectedElement;
    private Object selectedObject;
    private boolean treeSyncNeeded = false;
    private FilterPostProcessor filterPostProcessor;
    private InteractionType interactionType = InteractionType.Default;
    private Picture mousePicture = new Picture("MouseImage");
    private String insertAssetName = null;
    private LightModel lastSelectedLightModel = null;
    private float viewDistance;
    /*ui*/
    private Screen screen;
    private Button addCameraButton;
    private Menu texturesMenu;
    private ButtonAdapter selectButton;
    private ButtonAdapter cancelButton;
    private Geometry putTextureOn = null;
    private boolean updateMotionPath, allPathsShown = false;
    private boolean physicsPlaying = false;
    private Vector<AdditionalCameraDialog> additionalCameraDialogs = new Vector<AdditionalCameraDialog>();
    /*drag 'n drop*/
    private Node newParent;
    private Spatial newChild;
    private B3D_Node newB3DParent;
    private B3D_Spatial newB3DChild;

    /**
     *
     * @param camFrustumFar
     */
    public B3DApplication(float camFrustumFar)
    {
        viewDistance = camFrustumFar;
    }

    public void correctGridLocation()
    {
        gridGeometry.setLocalTranslation(
                Integer.parseInt((String) CurrentData.getConfiguration().getSettings().get("gridx"))
                * Float.parseFloat((String) CurrentData.getConfiguration().getSettings().get("gridgap")) / -2,
                0,
                Integer.parseInt((String) CurrentData.getConfiguration().getSettings().get("gridy"))
                * Float.parseFloat((String) CurrentData.getConfiguration().getSettings().get("gridgap")) / -2);
    }

    public void playPhysics()
    {
        physicsPlaying = true;
        Vector<Geometry> allObjects = new Vector<Geometry>();
        Wizard.insertAllGeometrys(sceneNode, allObjects);
        for (Geometry geometry : allObjects)
        {
            if (geometry.getControl(RigidBodyControl.class) != null)
            {
                bulletAppState.getPhysicsSpace().add(geometry.getControl(RigidBodyControl.class));
            }
        }
    }

    public void pausePhysics()
    {
        physicsPlaying = false;
        bulletAppState.getPhysicsSpace().removeAll(sceneNode);
    }

    public enum InteractionType
    {

        Default,
        InsertTexture,
        InsertModel
    };

    @Override
    public void simpleInitApp()
    {
        CurrentData.getEditorWindow().getFieldOfViewButton().setEnabled(true);
        CurrentData.setDefaultFieldOfView(new Float[]
        {
            cam.getFrustumBottom(),
            cam.getFrustumFar(),
            cam.getFrustumLeft(),
            cam.getFrustumNear(),
            cam.getFrustumRight(),
            cam.getFrustumTop()
        });
        initComponents();
        initBasicSettings();
        filterPostProcessor = new FilterPostProcessor(assetManager);
        if (CurrentData.getConfiguration().getSettings().get("showfilters").equals("true"))
        {
            viewPort.addProcessor(filterPostProcessor);
        }
        initInput();
        initMonkeyGUI();
        Wizard.setAssetManager(assetManager);
        Wizard.setCamera(cam);
        Wizard.setCameraNode(camNode);
        Wizard.setElementList(elementList);
    }

    /**
     *
     * @param tpf
     */
    @Override
    public void simpleUpdate(float tpf)
    {
        if (!CurrentData.isAppRunning())
        {
            CurrentData.setAppRunning(true);
        }
        if (newParent != null)
        {
            updatePairing();
        }
        updateEditNode();
        updateDragging();
        if (treeSyncNeeded)
        {
            CurrentData.getEditorWindow().getTree().sync();
            treeSyncNeeded = false;
        }
        mousePicture.setLocalTranslation(inputManager.getCursorPosition().x - 20, inputManager.getCursorPosition().y - 20, 10);
        updateAdditionalCameras();
    }

    private void initInput()
    {
        inputManager.addMapping("mouseLeft", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
        inputManager.addMapping("mouseRight", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
        inputManager.addMapping("mouseMiddle", new MouseButtonTrigger(MouseInput.BUTTON_MIDDLE));
        inputManager.addMapping("keyX", new KeyTrigger(KeyInput.KEY_X));
        inputManager.addMapping("keyY", new KeyTrigger(KeyInput.KEY_Y));
        inputManager.addMapping("keyZ", new KeyTrigger(KeyInput.KEY_C));
        inputManager.addListener(this, new String[]
        {
            "mouseLeft", "mouseRight", "mouseMiddle", "keyX", "keyY", "keyZ"
        });
    }

    private void initMonkeyGUI()
    {
        screen = new Screen(this);
        addCameraButton = new Button(screen, Vector2f.ZERO, new Vector2f(30, 30))
        {
            @Override
            public void onButtonMouseLeftDown(MouseButtonEvent evt, boolean toggled)
            {
                createCam();
            }

            @Override
            public void onButtonMouseRightDown(MouseButtonEvent evt, boolean toggled)
            {
            }

            @Override
            public void onButtonMouseLeftUp(MouseButtonEvent evt, boolean toggled)
            {
            }

            @Override
            public void onButtonMouseRightUp(MouseButtonEvent evt, boolean toggled)
            {
            }

            @Override
            public void onButtonFocus(MouseMotionEvent evt)
            {
            }

            @Override
            public void onButtonLostFocus(MouseMotionEvent evt)
            {
            }
        };
        addCameraButton.setFontSize(32);
        addCameraButton.setFontColor(ColorRGBA.Yellow);
        addCameraButton.setText("+");
        screen.addElement(addCameraButton);
        guiNode.addControl(screen);
    }

    private void initBasicSettings()
    {
        initView();
        initPhysics();
        assetManager.registerLocator(CurrentData.getProject().getAssetsFolder().getAbsolutePath(), FileLocator.class.getName());
    }

    private void initPhysics()
    {
        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);
    }

    private void initView()
    {
        if (CurrentData.getProject().getWorld().getViewPortColor() == null)
        {
            viewPort.setBackgroundColor(ColorRGBA.Black);
            CurrentData.getProject().getWorld().setViewPortColor(ColorRGBA.Black);
        } else
        {
            viewPort.setBackgroundColor(CurrentData.getProject().getWorld().getViewPortColor());
        }
        setPauseOnLostFocus(false);
        cam.setLocation(new Vector3f(0, 0, 0));
        flyCam.setDragToRotate(true);
        flyCam.setMoveSpeed(Float.parseFloat(CurrentData.getConfiguration().getSettings().get("camspeed").toString()));
        cam.setFrustumFar(viewDistance);
    }

    private void initComponents()
    {
        chaseCamera = new ChaseCamera(cam);
        camNode = new CameraNode("CamNode", cam);
        camNode.setControlDir(ControlDirection.SpatialToCamera);
        rootNode.attachChild(sceneNode);
        if (CurrentData.getConfiguration().getSettings().get("showscenery").equals("false"))
        {
            rootNode.attachChild(editorNode);
        }
        editorNode.attachChild(motionEventNode);
        editorNode.addLight(new AmbientLight());
        editorNode.addLight(editorNodeCamLight);
        selectionBox.setMaterial(new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"));
        selectionBox.getMaterial().setColor("Color", ColorRGBA.Blue);
        editorNode.attachChild(selectionBox);
        gridGeometry.setMaterial(new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"));
        gridGeometry.getMaterial().setColor("Color", ColorRGBA.DarkGray);
        gridGeometry.setShadowMode(RenderQueue.ShadowMode.Off);
        gridGeometry.setLocalTranslation(-10000, 0, -10000);
        mousePicture.setLocalScale(40, 40, 10);
        if (CurrentData.getConfiguration().getSettings().get("showgrid").equals("true"))
        {
            editorNode.attachChild(gridGeometry);
        }
        initArrows();
        correctGridLocation();
    }

    /**
     *
     * @param id
     */
    public void setSelectedElement(UUID id)
    {
        for (Map.Entry<Object, B3D_Element> element : elementList.getObjectHashMap().entrySet())
        {
            if (element.getValue().getID() == id)
            {
                selectedElement = element.getValue();
                setSelectedObject(element.getKey());
                break;
            }
        }
    }

    /**
     *
     * @param id
     */
    private void setSelectedObject(Object object)
    {
        CurrentData.getEditorWindow().getTree().setCodeSelect(true);
        CurrentData.getEditorWindow().getTree().updateSelection();
        if (lastSelectedLightModel != null)
        {
            lastSelectedLightModel.setSelected(false);
        }
        if (object instanceof Spatial)
        {
            Spatial tempSpatial = (Spatial) object;
            if (tempSpatial.getName().equals("LightModel"))
            {
                for (int i = 0; i < lightModels.size(); i++)
                {
                    if (lightModels.get(i).getNode().equals(tempSpatial))
                    {
                        setSelectedObject(lightModels.get(i).getLight());
                    }
                }
            } else
            {
                selectedObject = object;
            }
        } else
        {
            selectedObject = object;
            if (selectedObject instanceof Light)
            {
                for (int i = 0; i < lightModels.size(); i++)
                {
                    if (lightModels.get(i).getLight().equals(selectedObject))
                    {
                        lightModels.get(i).setSelected(true);
                        lastSelectedLightModel = lightModels.get(i);
                    }
                }
            }
        }
        CurrentData.getEditorWindow().getEditPane().arrange(false);
        if (selectedElement instanceof B3D_MotionEvent)
        {
            updateSelectedMotionPath();
        }
        updateSelectionBox();
    }

    /**
     *
     * @param id
     * @param spatial
     */
    public void setSelectedElement(UUID id, Spatial spatial)
    {
        if (id == null && spatial == null)
        {
            selectedElement = null;
            setSelectedObject(null);
        } else
        {
            for (Map.Entry<Object, B3D_Element> element : elementList.getObjectHashMap().entrySet())
            {
                if (element.getValue().getID().equals(id))
                {
                    setSelectedObject(element.getKey(), spatial);
                    selectedElement = element.getValue();
                    break;
                }
            }
        }
        CurrentData.getEditorWindow().getTree().updateSelection();
    }

    /**
     *
     * @param object
     * @param spatial
     */
    public void setSelectedObject(Object object, Spatial spatial)
    {
        CurrentData.getEditorWindow().getTree().setCodeSelect(true);
        CurrentData.getEditorWindow().getTree().updateSelection();
        if (lastSelectedLightModel != null)
        {
            lastSelectedLightModel.setSelected(false);
        }
        if (object instanceof Spatial)
        {
            Spatial tempSpatial = (Spatial) object;
            if (tempSpatial.getName().equals("LightModel"))
            {
                for (int i = 0; i < lightModels.size(); i++)
                {
                    if (lightModels.get(i).getNode().equals(tempSpatial))
                    {
                        setSelectedObject(lightModels.get(i).getLight());
                        break;
                    }
                }
            } else
            {
                selectedObject = object;
            }
            CurrentData.getEditorWindow().getEditPane().arrange(false);
        } else if (object instanceof MotionEvent)
        {
            selectedObject = spatial;
        } else
        {
            selectedObject = object;
            if (selectedObject instanceof Light)
            {
                for (int i = 0; i < lightModels.size(); i++)
                {
                    if (lightModels.get(i).getLight().equals(selectedObject))
                    {
                        lightModels.get(i).setSelected(true);
                        lastSelectedLightModel = lightModels.get(i);
                        break;
                    }
                }
            }
        }
        CurrentData.getEditorWindow().getEditPane().arrange(false);
        updateSelectionBox();
    }

    /**
     *
     * @param spatial
     */
    public void setSelectedElement(Spatial spatial)
    {
        if (spatial == null)
        {
            selectedElement = null;
            setSelectedObject(null);
        } else
        {
            for (Map.Entry<Object, B3D_Element> element : elementList.getObjectHashMap().entrySet())
            {
                if (element.getKey() == spatial && (selectedElement == null || !selectedElement.equals(element.getKey())))
                {
                    setSelectedObject(element.getKey());
                    selectedElement = element.getValue();
                    break;
                }
            }
        }
        CurrentData.getEditorWindow().getTree().updateSelection();
    }

    public void updateSelectionBox()
    {
        enqueue(new Callable<Void>()
        {
            public Void call() throws Exception
            {
                if (selectedObject instanceof Spatial)
                {
                    Spatial tempSpatial = (Spatial) selectedObject;
                    if (!tempSpatial.getName().equals("SkyBox") && tempSpatial != null)
                    {
                        if (tempSpatial.getWorldBound() != null && (tempSpatial.getWorldBound() instanceof BoundingBox))
                        {
                            selectionWireBox.fromBoundingBox((BoundingBox) tempSpatial.getWorldBound());
                        }
                        selectionBox.setLocalTranslation(tempSpatial.getWorldTranslation());
                        arrowNode.setLocalTranslation(tempSpatial.getWorldTranslation());
                    } else if (selectedObject instanceof Light)
                    {
                        for (int i = 0; i < lightModels.size(); i++)
                        {
                            if (lightModels.get(i).getLight().equals(selectedObject))
                            {
                                if (!(lightModels.get(i).getLight() instanceof AmbientLight))
                                {
//                      selectedElement = elementList.getElement(selectedObject);
//                      setSelectedObject(lightModels.get(i).getLight());
                        /*Select LightModelRep.*/
                                    selectionWireBox.fromBoundingBox((BoundingBox) lightModels.get(i).getRepresentative().getWorldBound());
                                    selectionBox.setLocalTranslation(lightModels.get(i).getRepresentative().getWorldTranslation());
                                    arrowNode.setLocalTranslation(selectionBox.getLocalTranslation());
                                }
                            }
                        }
                    }
                } else if (selectedObject instanceof AdditionalCameraDialog)
                {
                    Spatial tempSpatial = ((AdditionalCameraDialog) selectedObject).getRepresentative();
                    selectionBox.setLocalTranslation(tempSpatial.getWorldTranslation());
                    arrowNode.setLocalTranslation(tempSpatial.getWorldTranslation());
                }
                return null;
            }
        });
    }

    private void initArrows()
    {
        xArrow = assetManager.loadModel("Models/arrow.j3o");
        xArrow.setName("xArrow");
        xArrow.rotate(0, 0, FastMath.DEG_TO_RAD * 90);
        xArrow.setQueueBucket(RenderQueue.Bucket.Transparent);
        yArrow = assetManager.loadModel("Models/arrow.j3o");
        yArrow.setName("yArrow");
        yArrow.setQueueBucket(RenderQueue.Bucket.Transparent);
        zArrow = assetManager.loadModel("Models/arrow.j3o");
        zArrow.setName("zArrow");
        zArrow.rotate(FastMath.DEG_TO_RAD * 90, 0, 0);
        zArrow.setQueueBucket(RenderQueue.Bucket.Transparent);
        xArrowMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        xArrowMaterial.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
        xArrowMaterial.getAdditionalRenderState().setDepthTest(true);
        xArrowMaterial.setColor("Color", new ColorRGBA(1, 0, 0, .3f));
        yArrowMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        yArrowMaterial.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
        yArrowMaterial.getAdditionalRenderState().setDepthTest(true);
        yArrowMaterial.setColor("Color", new ColorRGBA(0, 0, 1, .3f));
        zArrowMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        zArrowMaterial.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
        zArrowMaterial.getAdditionalRenderState().setDepthTest(true);
        zArrowMaterial.setColor("Color", new ColorRGBA(0, 1, 0, .3f));
        xArrow.setMaterial(xArrowMaterial);
        yArrow.setMaterial(yArrowMaterial);
        zArrow.setMaterial(zArrowMaterial);
        arrowNode.attachChild(xArrow);
        arrowNode.attachChild(yArrow);
        arrowNode.attachChild(zArrow);
        editorNode.attachChild(arrowNode);
    }

    public void onAction(String name, boolean isPressed, float tpf)
    {
        if (name.equals("mouseLeft") && isPressed)
        {
            CollisionResults results = new CollisionResults();
            Vector2f click2d = inputManager.getCursorPosition();
            Vector3f click3d = cam.getWorldCoordinates(
                    new Vector2f(click2d.x, click2d.y), 0f).clone();
            Vector3f dir = cam.getWorldCoordinates(
                    new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d).normalizeLocal();
            Ray ray = new Ray(click3d, dir);
            rootNode.collideWith(ray, results);
            if (results.getClosestCollision() != null)
            {
                Geometry pickedGeometry = results.getClosestCollision().getGeometry();
                boolean checking = true;
                int cNumber = 0;
                while (checking)
                {
                    if (pickedGeometry.getName().equals("LightSymbol") || pickedGeometry.getName().contains("nonI_"))
                    {
                        try
                        {
                            if (results.getCollision(cNumber++).getGeometry() != null)
                            {
                                pickedGeometry = results.getCollision(cNumber).getGeometry();
                            }
                        } catch (java.lang.IndexOutOfBoundsException ioobe)
                        {
                            checking = false;
                        }
                    } else
                    {
                        checking = false;
                    }
                }
                System.out.println("Angeklickt: " + pickedGeometry.getName());
                if (interactionType.equals(InteractionType.Default))
                {
                    if (pickedGeometry.getParent().equals(arrowNode))
                    {
                        selectedArrow = pickedGeometry;
                        if (selectedArrow.equals(xArrow))
                        {
                            selectedArrow.setLocalScale(1.5f);
                            yArrow.setLocalScale(1);
                            zArrow.setLocalScale(1);
                        } else if (selectedArrow.equals(yArrow))
                        {
                            selectedArrow.setLocalScale(1.5f);
                            xArrow.setLocalScale(1);
                            zArrow.setLocalScale(1);
                        } else
                        {
                            selectedArrow.setLocalScale(1.5f);
                            xArrow.setLocalScale(1);
                            yArrow.setLocalScale(1);
                        }
                    } else if (pickedGeometry.getName().equals("cam") && editorNode.hasChild(pickedGeometry))
                    {
                        selectedElement = null;
                        for (AdditionalCameraDialog acd : additionalCameraDialogs)
                        {
                            if (acd.getRepresentative().hasChild(pickedGeometry))
                            {
                                selectedObject = acd;
                                CurrentData.getEditorWindow().getEditPane().arrange(true);
                                break;
                            }
                        }
                    } else if (pickedGeometry.getName().equals("LightModel"))
                    {
                        for (int i = 0; i < lightModels.size(); i++)
                        {
                            try
                            {
                                if (lightModels.get(i).getNode().equals(pickedGeometry)
                                        || lightModels.get(i).getRepresentative().equals(pickedGeometry))
                                {
                                    setSelectedObject(lightModels.get(i).getLight());
                                    for (B3D_Element element : elementList.getElementHashMap().keySet())
                                    {
                                        if (elementList.getElement(lightModels.get(i).getLight()).equals(element))
                                        {
                                            setSelectedElement(element.getID());
                                        }
                                    }
                                }
                            } catch (NullPointerException npe)
                            {
                                CurrentData.getObserverDialog().printError("Error picking LightModel", npe);
                            }
                        }
                    } else if (pickedGeometry.getName().equals("NodeModel"))
                    {
                        for (NodeModel nodeModel : nodeModels)
                        {
                            if (nodeModel.getModel().equals(pickedGeometry)
                                    || nodeModel.getModel().equals(pickedGeometry.getParent())
                                    || nodeModel.getModel().equals(pickedGeometry.getParent().getParent()))
                            {
                                setSelectedElement(elementList.getElement(nodeModel.getNode()).getID());
                            }
                        }
                    } else if (pickedGeometry.getName().equals("Waypoint"))
                    {
                        setSelectedElement(UUID.fromString((String) pickedGeometry.getUserData("B3D_ID")), pickedGeometry);
                    } else
                    {
                        selectedArrow = null;
                        xArrow.setLocalScale(1);
                        yArrow.setLocalScale(1);
                        zArrow.setLocalScale(1);
                        //System.out.println(":::" + pickedGeometry.getName() + ":::");
                        setSelectedElement(pickedGeometry);
                    }
                } else if (interactionType.equals(InteractionType.InsertTexture)
                        && !pickedGeometry.getName().equals("LightSymbol")
                        && !pickedGeometry.getName().contains("nonI_"))
                {
                    putTextureOn = pickedGeometry;
                    selectedElement = elementList.getElement(putTextureOn);
                    selectedObject = putTextureOn;
                    HashMap<String, String> tempHashMap = Wizard.readMat(new File("matD//" + pickedGeometry.getMaterial().getMaterialDef().getAssetName()));
                    ArrayList<String> textures = new ArrayList<String>();
                    for (Map.Entry<String, String> e : tempHashMap.entrySet())
                    {
                        if (e.getValue().equals("Texture") || e.getValue().equals("Texture2D"))
                        {
                            textures.add(e.getKey());
                        }
                    }
                    createTexturePopup(textures);
                    setInteractionType(InteractionType.Default, null);
                } else if (interactionType.equals(InteractionType.InsertModel))
                {
                    Spatial s = CurrentData.getEditorWindow().getB3DApplication().getAssetManager().loadModel(insertAssetName);
                    s.setUserData("angles", new Vector3f());
                    s.setUserData("scale", new Vector3f(1, 1, 1));
                    s.setUserData("modelName", insertAssetName);
                    s.setName("Model");
                    s.setLocalTranslation(cam.getLocation().add(cam.getDirection().mult(10)));
                    sceneNode.attachChild(s);
                    /*Set UserData to all children*/
                    if (s instanceof Node)
                    {
                        Vector<Geometry> geoms = new Vector<Geometry>();
                        Wizard.insertAllGeometrys((Node) s, geoms);
                        for (Geometry g : geoms)
                        {
                            g.setUserData("angles", new Vector3f());
                            g.setUserData("scale", new Vector3f(1, 1, 1));
                        }
                    }
                    interactionType = InteractionType.Default;
                }
            }
        } else if (name.equals("mouseMiddle") && isPressed)
        {
        } else if (name.equals("keyX"))
        {
            selectedArrow = xArrow;
            selectedArrow.setLocalScale(1.5f);
            yArrow.setLocalScale(1);
            zArrow.setLocalScale(1);
        } else if (name.equals("keyY"))
        {
            selectedArrow = yArrow;
            selectedArrow.setLocalScale(1.5f);
            xArrow.setLocalScale(1);
            zArrow.setLocalScale(1);
        } else if (name.equals("keyZ"))
        {
            selectedArrow = zArrow;
            selectedArrow.setLocalScale(1.5f);
            yArrow.setLocalScale(1);
            xArrow.setLocalScale(1);
        }
    }

    private void updateAdditionalCameras()
    {
        for (AdditionalCameraDialog acd : additionalCameraDialogs)
        {
            if (acd.initialized())
            {
                if (acd.getControlPanel().getFilterCheckBox().isSelected())
                {
                    if (!acd.getViewPort().getProcessors().contains(acd.getFilterPostProcessor()))
                    {
                        acd.getViewPort().addProcessor(acd.getFilterPostProcessor());
                    }
                } else
                {
                    if (acd.getViewPort().getProcessors().contains(acd.getFilterPostProcessor()))
                    {
                        acd.getViewPort().removeProcessor(acd.getFilterPostProcessor());
                    }
                }
            }
        }
    }

    private void updateDragging()
    {
        if (Mouse.isCreated())
        {
            if (Mouse.isButtonDown(1))
            {
                CollisionResults results = new CollisionResults();
                Vector2f click2d = inputManager.getCursorPosition();
                Vector3f click3d = cam.getWorldCoordinates(
                        new Vector2f(click2d.x, click2d.y), 0f).clone();
                Vector3f dir = cam.getWorldCoordinates(
                        new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d).normalizeLocal();
                Ray ray = new Ray(click3d, dir);
                if (selectedObject instanceof WaterFilter)
                {
                    yArrow.collideWith(ray, results);
                    CollisionResult closest = results.getClosestCollision();
                    if (closest != null)
                    {
                        WaterFilter selectedWaterFilter = (WaterFilter) selectedObject;
                        selectedWaterFilter.setWaterHeight(closest.getContactPoint().getY());
                    }
                } else
                {
                    rootNode.collideWith(ray, results);
                    boolean selectionLegit = false;
                    int resultNr = 0;
                    while (!selectionLegit)
                    {
                        if (results.size() > resultNr)
                        {
                            if (!results.getCollision(resultNr).getGeometry().getName().equals("LightSymbol")
                                    && !results.getCollision(resultNr).getGeometry().getName().contains("nonI_"))
                            {
                                selectionLegit = true;
                            } else
                            {
                                resultNr++;
                            }
                        } else
                        {
                            return;
                        }
                    }
                    if (results.getCollision(resultNr) != null && selectedObject != null)
                    {
                        final Vector3f arrowBefore = arrowNode.getWorldTranslation().clone();
                        float xDiff = 0, yDiff = 0, zDiff = 0;
                        if (selectedArrow != null)
                        {
                            if (selectedArrow.equals(xArrow))
                            {
                                xDiff = results.getCollision(resultNr).getContactPoint().getX() - arrowNode.getLocalTranslation().getX();
                                arrowNode.setLocalTranslation(new Vector3f(
                                        results.getCollision(resultNr).getContactPoint().getX(),
                                        arrowNode.getLocalTranslation().getY(),
                                        arrowNode.getLocalTranslation().getZ()));
                            } else if (selectedArrow.equals(yArrow))
                            {
                                yDiff = results.getCollision(resultNr).getContactPoint().getY() - arrowNode.getLocalTranslation().getY();
                                arrowNode.setLocalTranslation(new Vector3f(
                                        arrowNode.getLocalTranslation().getX(),
                                        results.getCollision(resultNr).getContactPoint().getY(),
                                        arrowNode.getLocalTranslation().getZ()));
                            } else
                            {
                                zDiff = results.getCollision(resultNr).getContactPoint().getZ() - arrowNode.getLocalTranslation().getZ();
                                arrowNode.setLocalTranslation(new Vector3f(
                                        arrowNode.getLocalTranslation().getX(),
                                        arrowNode.getLocalTranslation().getY(),
                                        results.getCollision(resultNr).getContactPoint().getZ()));
                            }
                            //Vector3f difference = arrowNode.getWorldTranslation().subtract(arrowBefore);
                            if (selectedObject instanceof Spatial)
                            {
                                Spatial tempSpatial = (Spatial) selectedObject;
                                if (tempSpatial.getName().equals("Waypoint"))
                                {
                                    /*UUID id = UUID.fromString((String) tempSpatial.getUserData("B3D_ID"));
                                     B3D_Element b3d_MotionPath = null;
                                     for (Map.Entry<Object, B3D_Element> element : elementList.getObjectHashMap().entrySet())
                                     {
                                     if (element.getValue().getID().equals(id))
                                     {
                                     b3d_MotionPath = element.getValue();
                                     }
                                     }*/
                                    MotionEvent motionEvent = (MotionEvent) elementList.getObject(selectedElement);
                                    motionEvent.getPath().getWayPoint((Integer) tempSpatial.getUserData("B3D_INDEX")).set(arrowNode.getWorldTranslation());
                                    updateSelectedMotionPath();
                                }
                                tempSpatial.move(new Vector3f(xDiff, yDiff, zDiff));
                            }/*Auch LightModels verschieben!*/ else if (selectedObject instanceof Light)
                            {
                                for (int i = 0; i < lightModels.size(); i++)
                                {
                                    if (lightModels.get(i).getLight().equals(selectedObject))
                                    {
                                        if (selectedObject instanceof DirectionalLight)
                                        {
                                            lightModels.get(i).getNode().setLocalTranslation(arrowNode.getWorldTranslation());
                                        } else if (selectedObject instanceof SpotLight)
                                        {
                                            lightModels.get(i).getRepresentative().setLocalTranslation(arrowNode.getWorldTranslation());
                                            Vector3f end = lightModels.get(i).getRepresentative().getWorldTranslation().add(
                                                    ((SpotLight) lightModels.get(i).getLight()).getDirection().mult(((SpotLight) lightModels.get(i).getLight()).getSpotRange()));
                                            Mesh mesh = new Line(arrowNode.getWorldTranslation(), arrowNode.getWorldTranslation().add(((SpotLight) lightModels.get(i).getLight()).getDirection().mult(((SpotLight) lightModels.get(i).getLight()).getSpotRange())));
                                            mesh.setPointSize(5);
                                            lightModels.get(i).getSymbol().setMesh(mesh);
                                            ((SpotLight) selectedObject).setPosition(lightModels.get(i).getRepresentative().getLocalTranslation());
                                        } else if (selectedObject instanceof PointLight)
                                        {
                                            lightModels.get(i).getRepresentative().setLocalTranslation(arrowNode.getWorldTranslation());
                                            ((PointLight) selectedObject).setPosition(lightModels.get(i).getRepresentative().getLocalTranslation());
                                        }
                                    }
                                }
                            } else if (selectedObject instanceof AdditionalCameraDialog)
                            {
                                ((AdditionalCameraDialog) selectedObject).getRepresentative().setLocalTranslation(arrowNode.getWorldTranslation());
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     *
     * @param interactionType
     * @param objectName
     */
    public void setInteractionType(InteractionType interactionType, String objectName)
    {
        if (interactionType.equals(InteractionType.Default))
        {
            guiNode.detachChild(mousePicture);
        } else if (interactionType.equals(InteractionType.InsertTexture))
        {
            mousePicture.setTexture(assetManager, (Texture2D) assetManager.loadTexture(objectName), true);
            insertAssetName = objectName;
            guiNode.attachChild(mousePicture);
        }
        this.interactionType = interactionType;
    }

    private void createTexturePopup(ArrayList<String> textures)
    {
        texturesMenu = new Menu(screen, inputManager.getCursorPosition(), false)
        {
            @Override
            public void onMenuItemClicked(int index, Object value, boolean isToggled)
            {
                if (elementList.getElement(putTextureOn) != null)
                {
                    putTextureOn.getMaterial().setTexture(value.toString(), assetManager.loadTexture(insertAssetName));
                    try
                    {
                        if (((B3D_Geometry) elementList.getElement(putTextureOn)).getMaterial().getPropertyList().has(value.toString()))
                        {
                            ((B3D_Geometry) elementList.getElement(putTextureOn)).getMaterial().getPropertyList().change(value.toString(), insertAssetName);
                        } else
                        {
                            ((B3D_Geometry) elementList.getElement(putTextureOn)).getMaterial().getPropertyList().add(value.toString(), "Texture", insertAssetName);
                        }
                        screen.removeElement(cancelButton);
                        screen.removeElement(selectButton);
                        screen.removeElement(texturesMenu);
                        screen.removeElement(this);
                        CurrentData.getEditorWindow().getEditPane().arrange(true);
                    } catch (NullPointerException npe)
                    {
                        CurrentData.getObserverDialog().printError("Error putting texture / removing In-Editor-GUI", npe);
                    }
                }
            }
        };
        for (String texture : textures)
        {
            texturesMenu.addMenuItem(texture, texture, null);
        }
        selectButton = new ButtonAdapter(screen, inputManager.getCursorPosition())
        {
            @Override
            public void onButtonMouseLeftUp(MouseButtonEvent evt, boolean isToggled)
            {
                texturesMenu.showMenu(null, getAbsoluteX(), getAbsoluteY() - texturesMenu.getHeight());
            }
        };
        selectButton.setText(" Select Type: ");
        cancelButton = new ButtonAdapter(screen, new Vector2f(inputManager.getCursorPosition().getX(), inputManager.getCursorPosition().getY() + selectButton.getHeight()))
        {
            @Override
            public void onButtonMouseLeftUp(MouseButtonEvent evt, boolean isToggled)
            {
                screen.removeElement(texturesMenu);
                screen.removeElement(selectButton);
                screen.removeElement(this);
            }
        };
        cancelButton.setText(" Cancel ");
        screen.addElement(cancelButton);
        screen.addElement(selectButton);
        screen.addElement(texturesMenu);
    }

    private void updateEditNode()
    {
        /*if (selectedObject instanceof Spatial)
         {
         arrowNode.setLocalRotation(((Spatial) selectedObject).getParent().getWorldRotation());
         }*/
        editorNodeCamLight.setColor(ColorRGBA.LightGray);
        editorNodeCamLight.setDirection(cam.getDirection());
        updateNodes();
        updateSelectionBox();
        updateObjectScales();
        if (selectedObject instanceof WaterFilter)
        {
            updateYArrow();
        } else
        {
            arrowNode.attachChild(xArrow);
            arrowNode.attachChild(zArrow);
        }
        if (CurrentData.getEditorWindow().isUndecorated())
        {
            if (!screen.getElements().contains(addCameraButton))
            {
                screen.addElement(addCameraButton);
                addCameraButton.setPosition(0, CurrentData.getEditorWindow().getPlayPanel().getSize().height - 30);
            }
        } else
        {
            screen.removeElement(addCameraButton);
        }
        if (CurrentData.getConfiguration().getSettings().get("showallmotionpaths").equals("true") && !allPathsShown)
        {
            updateAllMotionPathGeometrys();
        }
        if (updateMotionPath && selectedElement instanceof B3D_MotionEvent)
        {
            updateMotionPathGeometrys();
        } else if (!(selectedElement instanceof B3D_MotionEvent))
        {
            motionEventNode.detachAllChildren();
        }
        /*if (selectedObject instanceof Spatial && !(((Spatial) selectedObject).getParent().equals(sceneNode)))
         {
         //arrowNode.setLocalRotation(((Spatial) selectedObject).getParent().getWorldRotation());
         ((Spatial) selectedObject).getParent().attachChild(arrowNode);
         } else
         {
         editorNode.attachChild(arrowNode);
         }*/
    }

    private void updateNodes()
    {
        synchronized (nodeModels)
        {
            for (NodeModel nodeModel : nodeModels)
            {
                long startTime = System.nanoTime();
                //nodeModel.update(nodeModel.getNode().equals(selectedObject));
                System.out.println("\nTotal time: \t" + (System.nanoTime() - startTime) + " mils");
                //if (!editorNode.hasChild(nodeModel.getModel()))
                //{
                //    editorNode.attachChild(nodeModel.getModel());
                //}
            }
        }
    }

    private void updateYArrow()
    {
        arrowNode.detachChild(xArrow);
        arrowNode.detachChild(zArrow);
        yArrow.setMaterial(yArrowMaterial);
        WaterFilter tempWaterFilter = (WaterFilter) selectedObject;
        arrowNode.setLocalTranslation(cam.getLocation().add(cam.getDirection().multLocal(cam.getLocation().getY() * 2.5f - tempWaterFilter.getWaterHeight())));
        arrowNode.setLocalTranslation(arrowNode.getLocalTranslation().getX(),
                tempWaterFilter.getWaterHeight(),
                arrowNode.getLocalTranslation().getZ());
    }

    private void updateMotionPathGeometrys()
    {
        if (CurrentData.getConfiguration().getSettings().get("showallmotionpaths").equals("true"))
        {
            updateAllMotionPathGeometrys();
        } else
        {
            if (selectedElement instanceof B3D_MotionEvent)
            {
                motionEventNode.detachAllChildren();
                updateMotionPaths((B3D_MotionEvent) selectedElement, (MotionEvent) elementList.getObject(selectedElement));
            } else
            {
                motionEventNode.detachAllChildren();
            }
            updateMotionPath = false;
            allPathsShown = false;
        }

    }

    private void updateAllMotionPathGeometrys()
    {
        motionEventNode.detachAllChildren();
        for (MotionEvent motionEvent : motionEvents)
        {
            updateMotionPaths((B3D_MotionEvent) elementList.getElement(motionEvent), motionEvent);
        }
        allPathsShown = true;
    }

    private void updateMotionPaths(B3D_MotionEvent b3D_Spatial_MotionPath, MotionEvent event)
    {
        if (motionEventNode.getChild(b3D_Spatial_MotionPath.getID().toString()) != null)
        {
            motionEventNode.detachChild(motionEventNode.getChild(b3D_Spatial_MotionPath.getID().toString()));
        }
        Node pathNode = new Node(b3D_Spatial_MotionPath.getID().toString());
        Vector3f[] waypoints = new Vector3f[event.getPath().getNbWayPoints()];
        for (int i = 0; i < event.getPath().getNbWayPoints(); i++)
        {
            waypoints[i] = event.getPath().getWayPoint(i);
        }
        Spline spline = new Spline(Spline.SplineType.CatmullRom, waypoints, b3D_Spatial_MotionPath.getMotionPath().getCurveTension(), b3D_Spatial_MotionPath.getMotionPath().isCycle());
        Curve curve = new Curve(spline, 100);
        Geometry curveGeometry = new Geometry("Curve", curve);
        curveGeometry.setMaterial(new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"));
        curveGeometry.getMaterial().setColor("Color", b3D_Spatial_MotionPath.getMotionPath().getColor());
        pathNode.attachChild(curveGeometry);
        for (int i = 0; i < waypoints.length; i++)
        {
            Geometry pointGeometry = new Geometry("Waypoint", new Sphere(20, 20, 1));
            pointGeometry.setUserData("B3D_ID", b3D_Spatial_MotionPath.getID().toString());
            pointGeometry.setUserData("B3D_INDEX", new Integer(i));
            pointGeometry.setLocalTranslation(waypoints[i]);
            pointGeometry.setMaterial(new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"));
            pointGeometry.getMaterial().setColor("Diffuse", b3D_Spatial_MotionPath.getMotionPath().getColor());
            pointGeometry.setLocalScale(cam.getLocation().distance(pointGeometry.getWorldTranslation()) / 55);
            pathNode.attachChild(pointGeometry);
        }
        motionEventNode.attachChild(pathNode);
    }

    private void updateObjectScales()
    {
        arrowNode.setLocalScale(cam.getLocation().distance(arrowNode.getLocalTranslation()) / 55);
    }

    private void updatePairing()
    {
        newChild.getParent().detachChild(newChild);
        sceneNode.attachChild(newChild);
        newChild = null;
        newParent = null;
    }

    public void prepairPairing(B3D_Node parent, B3D_Spatial child)
    {
        newB3DChild = child;
        newB3DParent = parent;
    }

    public void prepairSceneNodePairing(B3D_Spatial child)
    {
        newB3DChild = child;
        newB3DParent = null;
    }

    public void startPairing()
    {
        if (newB3DChild != null)
        {
            if (newB3DParent == null)
            {
                pairWithSceneNode();
            } else
            {
                pair();
            }
        }
    }

    private void pair()
    {
        if (newB3DChild.getParentID() != null)
        {
            B3D_Node oldParent = (B3D_Node) elementList.getElement(newB3DChild.getParentID());
            oldParent.getChildren().remove(newB3DChild);
        }
        newB3DParent.getChildren().add(newB3DChild);
        newB3DChild.setParentID(newB3DParent.getID());
        newParent = (Node) elementList.getObject(newB3DParent);
        selectedNode = newParent;
        selectedElement = newB3DParent;
        newChild = (Spatial) elementList.getObject(newB3DChild);
    }

    private void pairWithSceneNode()
    {
        if (newB3DChild.getParentID() != null)
        {
            B3D_Node oldParent = (B3D_Node) elementList.getElement(newB3DChild.getParentID());
            oldParent.getChildren().remove(newB3DChild);
        }
        newB3DChild.setParentID(null);
        newParent = sceneNode;
        selectedNode = sceneNode;
        newChild = (Spatial) elementList.getObject(newB3DChild);
        newChild.setUserData("parentID", null);
    }

    public void cancelPairing()
    {
        newB3DChild = null;
    }

    public FilterPostProcessor getFilterPostProcessor()
    {
        return filterPostProcessor;
    }

    public InteractionType getInteractionType()
    {
        return interactionType;
    }

    public Node getMotionEventNode()
    {
        return motionEventNode;
    }

    public B3D_Element getSelectedElement()
    {
        return selectedElement;
    }

    public Object getSelectedObject()
    {
        return selectedObject;
    }

    public Geometry getSelectionBox()
    {
        return selectionBox;
    }

    public Vector<LightModel> getLightModels()
    {
        return lightModels;
    }

    public AppSettings getSettings()
    {
        return settings;
    }

    /**
     *
     * @param insertAssetName
     */
    public void setInsertAssetName(String insertAssetName)
    {
        this.insertAssetName = insertAssetName;
    }

    public Vector<LightScatteringModel> getLightScatteringModels()
    {
        return lightScatteringModels;
    }

    public Vector<MotionEvent> getMotionEvents()
    {
        return motionEvents;
    }

    public void attachCam()
    {
        flyCam.setEnabled(false);
        rootNode.attachChild(camNode);
    }

    public void detachCam()
    {
        flyCam.setEnabled(true);
        rootNode.detachChild(camNode);
    }

    public CameraNode getCamNode()
    {
        return camNode;
    }

    public ElementList getElementList()
    {
        return elementList;
    }

    public SceneNode getSceneNode()
    {
        return sceneNode;
    }

    /**
     *
     * @param sceneNode
     */
    public void setSceneNode(SceneNode sceneNode)
    {
        this.sceneNode = sceneNode;
    }

    public Node getEditorNode()
    {
        return editorNode;
    }

    /**
     *
     * @param editorNode
     */
    public void setEditorNode(Node editorNode)
    {
        this.editorNode = editorNode;
    }

    public Geometry getGridGeometry()
    {
        return gridGeometry;
    }

    /**
     *
     * @param gridGeometry
     */
    public void setGridGeometry(Geometry gridGeometry)
    {
        this.gridGeometry = gridGeometry;
    }

    /**
     *
     * @param sync
     */
    public void setSyncTree(boolean sync)
    {
        treeSyncNeeded = sync;
    }

    public BulletAppState getBulletAppState()
    {
        return bulletAppState;
    }

    public void updateSelectedMotionPath()
    {
        updateMotionPath = true;
    }

    public Node getSelectedNode()
    {
        return selectedNode;
    }

    public void setSelectedNode(Node selectedNode)
    {
        this.selectedNode = selectedNode;
    }

    public Vector<NodeModel> getNodeModels()
    {
        return nodeModels;
    }

    public boolean isPhysicsPlaying()
    {
        return physicsPlaying;
    }

    public Node getNewParent()
    {
        return newParent;
    }

    public void createCam()
    {
        AdditionalCameraDialog acd = new AdditionalCameraDialog(cam.getLocation(), cam.getRotation(),
                CurrentData.getEditorWindow().getPlayPanel().getSize().width / 2,
                CurrentData.getEditorWindow().getPlayPanel().getSize().height / 2,
                renderer,
                renderManager,
                sceneNode,
                assetManager,
                true);
    }

    public void addAdditionalCamera(final AdditionalCameraDialog acd)
    {
        enqueue(new Callable<Void>()
        {
            public Void call() throws Exception
            {
                editorNode.attachChild(acd.getRepresentative());
                return null;
            }
        });
        additionalCameraDialogs.add(acd);
    }

    public void removeAdditionalCamera(AdditionalCameraDialog acd)
    {
        editorNode.detachChild(acd.getRepresentative());
        additionalCameraDialogs.remove(acd);
    }

    public Vector<AdditionalCameraDialog> getAdditionalCameraDialogs()
    {
        return additionalCameraDialogs;
    }
}